# app/api/v2/services.py
from sqlalchemy.orm import Session

from app.map_planner.models import Warehouse1 as Warehouse1Model
from app.map_planner.models import Warehouse2 as Warehouse2Model
from app.map_planner.mapper import MapBase
from app.map_planner.algorithm import TSP
from . import schemas
from app.core import Warehouse


class Services:
    def __init__(self) -> None:
        self.tsp = TSP()
        self.map_base = MapBase()

    def init_warehouses(
            self,
            db: Session
    ) -> list:
        """初始化数据库。
        
        Args:
            db (Session): 数据库会话
        
        Returns:
            list: [成功标志, 信息或数据]
        """

        # 创建地图实例
        # Create map instance
        map_base = MapBase()

        # 获取地图信息
        # Get map info
        wh_1_map_list = map_base.locstr_for_db(
            warehouse_id=1,
            loc_tuples=map_base.coordinates
            )
        wh_2_map_list = map_base.locstr_for_db(
            warehouse_id=2,
            loc_tuples=map_base.coordinates
            )
        
        # 创建禁止库位
        # Create ban list
        ban_list = {
            "1,1,1", "1,2,1", "1,1,28", "1,2,28",
            "2,1,1", "2,2,1", "2,1,28", "2,2,28"
            }

        # 清空现有数据
        # Clear existing data
        db.query(Warehouse1Model).delete()
        db.query(Warehouse2Model).delete()

        # 生成数据
        # Create data
        locations = list()
        for idx, loc in enumerate(wh_1_map_list):
            
            if loc in ban_list:
                status = -1
            else:
                status = 0
            
            locations.append(Warehouse1Model(
                id=idx, 
                location=loc,
                pallet_id=None,
                status=status
            ))

        for idx, loc in enumerate(wh_2_map_list):
            
            if loc in ban_list:
                status = -1
            else:
                status = 0
            
            locations.append(Warehouse2Model(
                id=idx, 
                location=loc,
                pallet_id=None,
                status=status
            ))

        # 批量插入数据
        # Batch insert data
        db.add_all(locations)
        db.commit()

        # 获取库位信息
        # Get warehouse info
        warehouse1_info = db.query(Warehouse1Model).all()
        warehouse2_info = db.query(Warehouse2Model).all()

        if not warehouse1_info and not warehouse2_info:
            return [False, "无库位信息"]
        return [True, [warehouse1_info, warehouse2_info]]
    
    def get_warehouse(
            self, 
            db: Session,
            warehouse_id: int
    ) -> list:
        """获取库区所有信息。
        
        Args:
            db (Session): 数据库会话
            
        Returns:
            list: [成功标志, 信息或数据]
        """
        if warehouse_id == 1:
            WarehouseModel = Warehouse1Model
        elif warehouse_id == 2:
            WarehouseModel = Warehouse2Model
        else:
            return [False, "查询库排号不存在"]
        location_info = db.query(WarehouseModel).all()
        if not location_info:
            return [False, "无库位信息"]
        return [True, location_info]
    
    def get_location_by_loc(
            self, 
            db: Session, 
            wh_location: str
    ) -> list:
        """根据库位坐标, 获取库位信息。

        Args:
            db (Session): 数据库会话
            wh_location: 库位坐标 (格式: 'wh,x,y' - '库区,层,列')
            
        Returns:
            list: [成功标志, 信息或数据]
        """

        warehouse = self.map_base.split_location_by_loc(wh_location)['warehouse']
        
        if warehouse == 1:
            LocationModel = Warehouse1Model
        elif warehouse == 2:
            LocationModel = Warehouse2Model
        else:
            return [False, "查询库排号不存在"]
        
        location_info = db.query(LocationModel).filter(LocationModel.location == wh_location).first()
        if not location_info:
            return [False, "无库位信息"]
        return [True, location_info]
    
    def get_location_by_pallet_id(
            self, 
            db: Session, 
            pallet_id: str
    ) -> list:
        """根据托盘号, 获取库位信息。

        Args:
            db (Session): 数据库会话
            pallet_id: 托盘号

        Returns:
            list: [成功标志, 信息或数据]
        """

        warehouse_1_info = db.query(Warehouse1Model).filter(Warehouse1Model.pallet_id == pallet_id).all()
        warehouse_2_info = db.query(Warehouse2Model).filter(Warehouse2Model.pallet_id == pallet_id).all()
        
        if not warehouse_1_info and not warehouse_2_info:
            return [False, "无库位信息"]
        return [True, [warehouse_1_info, warehouse_2_info]]

    def get_location_by_status(
            self, 
            db: Session, 
            status: int
    ) -> list:
        """根据库位状态, 获取库位信息。

        Args:
            db (Session): 数据库会话
            status: 库位状态

        Returns:
            list: [成功标志, 信息或数据]
        """
        
        warehouse_1_info = db.query(Warehouse1Model).filter(Warehouse1Model.status == status).all()
        warehouse_2_info = db.query(Warehouse2Model).filter(Warehouse2Model.status == status).all()

        if not warehouse_1_info and not warehouse_2_info:
            return [False, "无库位信息"]
        return [True, [warehouse_1_info, warehouse_2_info]]
    
    def update_pallet_by_loc(
            self, 
            db: Session, 
            wh_location: str,
            pallet_id: str
    ) -> list:
        """用库位坐标和托盘号, 更新库位信息。

        Args:
            db (Session): 数据库会话
            wh_location: 库位坐标 (格式: 'wh,x,y' - '库区,层,列')
            pallet_id: 托盘号

        Returns:
            list: [成功标志, 信息或数据]
        """
        disable_locations = [
            "1,1,1", "1,2,1", "1,1,28", "1,2,28",
            "2,1,1", "2,2,1", "2,1,28", "2,2,28"
            ]
        if wh_location in disable_locations:
            return [False, "库位已禁用"]
        
        warehouse = self.map_base.split_location_by_loc(wh_location)['warehouse']
        
        if warehouse == 1:
            LocationModel = Warehouse1Model
        elif warehouse == 2:
            LocationModel = Warehouse2Model
        else:
            return [False, "查询库排号不存在"]
        
        # 查询库位信息 匹配是否存在
        location_info = db.query(LocationModel).filter(LocationModel.location == wh_location).first()
        if not location_info:
            return [False, "无库位信息"]
        
        # 写入库位托盘号
        location_info.pallet_id = pallet_id
        location_info.status = Warehouse.CARGO.value

        # Commit changes and refresh
        db.commit()
        db.refresh(location_info)
        return [True, location_info]

    def delete_pallet_by_loc(
            self, 
            db: Session, 
            wh_location: str
            ) -> list:
        """用库位坐标, 删除库位托盘号。

        Args:
            db (Session): 数据库会话
            wh_location: 库位坐标 (格式: 'wh,x,y' - '库区,层,列')

        Returns:
            list: [成功标志, 信息或数据]
        """
        disable_locations = [
            "1,1,1", "1,2,1", "1,1,28", "1,2,28",
            "2,1,1", "2,2,1", "2,1,28", "2,2,28"
            ]
        if wh_location in disable_locations:
            return [False, "库位已禁用"]
        
        warehouse = self.map_base.split_location_by_loc(wh_location)['warehouse']
        
        if warehouse == 1:
            LocationModel = Warehouse1Model
        elif warehouse == 2:
            LocationModel = Warehouse2Model
        else:
            return [False, "查询库排号不存在"]
        
        # Check if location exists
        location_info = db.query(LocationModel).filter(LocationModel.location == wh_location).first()
        if not location_info:
            return [False, "无库位信息"]
        
        # Update pallet ID and status
        location_info.pallet_id = None
        location_info.status = Warehouse.FREE.value
        
        # Commit changes and refresh
        db.commit()
        db.refresh(location_info)
        return [True, location_info]
    
    def task_inband(self, db: Session, request: schemas.TaskRequest):
        """入库任务。

        Args:
            db (Session): 数据库会话
            request: 入库任务请求体
        
        Returns:
            list: [成功标志, 任务列表或错误信息]
        """
        # 空任务列表
        resp_list = []

        # 堆垛机位置
        stacker_pos = request.stacker_location

        # 优化任务顺序
        task_loc_list = [task.description[0].target_location for task in request.tasks]
        optimal_task_list = self.tsp.advanced_nearest_neighbor(stacker_pos, task_loc_list)
        task_list = sorted(request.tasks, key=lambda x: optimal_task_list.index(x.description[0].target_location))
        
        for task in task_list:
            task_no = task.task_no
            task_desc = task.description[0]
            task_type = task_desc.task_type
            gate_pos = task_desc.gate_location
            target_pos = task_desc.target_location

            # 入库
            if task_type == 0:
                stacker_to_gate_path = [1, stacker_pos, gate_pos]
                gate_to_target_path = [2, gate_pos, target_pos]
                resp_list.append({
                    "task_no": task_no,
                    "task_type": task_type,
                    "path_1": stacker_to_gate_path,
                    "path_2": gate_to_target_path,
                    "end_location": target_pos,
                })
                # 数据库更新
                self.update_pallet_by_loc(
                    db=db,
                    wh_location=target_pos,
                    pallet_id=task_no
                )
                # 堆垛机最终位置
                stacker_pos = target_pos

            else:
                return [False, "任务类型错误"]

        return [True, resp_list]
    
    def task_outband(self, db: Session, request: schemas.TaskRequest):
        """出库任务。

        Args:
            db (Session): 数据库会话
            request: 出库任务请求体

        Returns:
            list: [成功标志, 任务列表或错误信息]
        """
        # 空任务列表
        resp_list = []

        # 堆垛机位置
        stacker_pos = request.stacker_location

        # 优化任务顺序
        task_loc_list = [task.description[0].target_location for task in request.tasks]
        optimal_task_list = self.tsp.advanced_nearest_neighbor(stacker_pos, task_loc_list)
        task_list = sorted(request.tasks, key=lambda x: optimal_task_list.index(x.description[0].target_location))
        
        for task in task_list:
            task_no = task.task_no
            task_desc = task.description[0]
            task_type = task_desc.task_type
            gate_pos = task_desc.gate_location
            target_pos = task_desc.target_location

            # 入库
            if task_type == 1:
                stacker_to_target_path = [1, stacker_pos, target_pos]
                target_to_gate_path = [2, target_pos, gate_pos]
                resp_list.append({
                    "task_no": task_no,
                    "task_type": task_type,
                    "path_1": stacker_to_target_path,
                    "path_2": target_to_gate_path,
                    "end_location": gate_pos,
                })
                # 数据库更新
                self.delete_pallet_by_loc(
                    db=db,
                    wh_location=target_pos
                )
                # 堆垛机最终位置
                stacker_pos = gate_pos

            else:
                return [False, "任务类型错误"]

        return [True, resp_list]
    

    def cargo_move(self, db: Session, request: schemas.CarGoMoveRequest):
        """货物转移任务。
        
        Args:
            db (Session): 数据库会话
            request: 货物转移任务请求体

        Returns:
            list: [成功标志, 任务列表或错误信息]
        """
        # 空任务列表
        resp_list = []

        # 堆垛机位置
        stacker_pos = request.stacker_location

        # 优化任务顺序
        task_loc_list = [task.description[0].target_location for task in request.tasks]
        optimal_task_list = self.tsp.advanced_nearest_neighbor(stacker_pos, task_loc_list)
        task_list = sorted(request.tasks, key=lambda x: optimal_task_list.index(x.description[0].target_location))
        
        for task in task_list:
            task_no = task.task_no
            task_desc = task.description[0]
            task_type = task_desc.task_type
            current_pos = task_desc.current_location
            target_pos = task_desc.target_location

            # 货物转移
            if task_type == 2:
                # # 查询数据库
                # current_location_info = self.get_location_by_loc(
                #     db=db,
                #     LOCATION=current_pos,
                #     WAREHOUSE=current_whs
                # )
                # if current_location_info[0] == False:
                #     return [False, f"任务{task_no}失败, 原库位信息查询失败: {current_location_info[1]}"]
                # elif current_location_info[0]:
                #     cur_loc_info = current_location_info[1]
                #     old_pallet_id = cur_loc_info.pallet_id

                stacker_to_target_path = [1, stacker_pos, current_pos]
                target_to_gate_path = [2, current_pos, target_pos]
                resp_list.append({
                    "task_no": task_no,
                    "task_type": task_type,
                    "path_1": stacker_to_target_path,
                    "path_2": target_to_gate_path,
                    "end_location": target_pos,
                })
                # 数据库更新
                self.delete_pallet_by_loc(
                    db=db,
                    wh_location=current_pos
                )
                self.update_pallet_by_loc(
                    db=db,
                    wh_location=target_pos,
                    pallet_id=task_no
                )
                # 堆垛机最终位置
                stacker_pos = target_pos

            else:
                return [False, "任务类型错误"]

        return [True, resp_list]